﻿{$DEFINE MSSQL}
unit UModel;

interface

uses
  DataLayer, System.SysUtils,
  System.Classes, Data.FMTBcd, Data.DB, FMX.Grid, Datasnap.DBClient,
  Data.SqlExpr, System.Generics.Collections,
  ClientClassesUnit1, ClientModuleUnit1;

type

  // declare the Interface to display a page items on buttons
  IDispAPageItemsProc = Interface
    procedure DisplayAPageMenuItems(page: integer);
    procedure DisplayAPageCategoryItems(page: integer);
    procedure DisplayAPageInstructionItems(page: integer);

  End;

  TPriceType = (Normal, Weekend, HappyHour);

  TProfile = class
  private
    FPriceType: TPriceType;
  public
    ForceVIPDiscount: boolean;
    NotAllowModify: boolean;
    RoundingFlag: smallint;
    ServiceChargeRate: double;
    HappyHour: boolean;
    HappyHourStartTime: TDatetime;
    HappyHourEndTime: TDatetime;
    EnableWeekendPriceFunction: boolean;
    WeekendPriceStartDay: integer;
    WeekendPriceEndDay: integer;
    property PriceType: TPriceType read FPriceType;
    constructor Create;
  end;

  TMenuItem = class
  public
    Code, Description, Category: String;
    PriceList: TList<TPair<String, Currency>>;
    TaxRate: double;
    constructor Create(q: TClientDataSet);

  end;

  TPagingObject<T: class> = class(TObject)
  private
  protected
    FDisplayAPageProc: IDispAPageItemsProc;
    CurrentPage: integer;
    LastPage: integer;
    ItemNumberPerPage: integer;
    procedure LoadItems; virtual; abstract;
    procedure RefreshUI; virtual; abstract;
    procedure CalcLastPage;
  public
    List: TList<T>;
    procedure NextPage;
    procedure PreviousPage;
    procedure FirstPage;
    function SelectAnItem(key: string): T;
    constructor Create(ItemNumberPerPage: integer;
      DisplayAPageProc: IDispAPageItemsProc);
    destructor Destroy; override;
    property DisplayAPageMenuProc: IDispAPageItemsProc read FDisplayAPageProc
      write FDisplayAPageProc;
  end;

  TCategoryItem = class
  public
    Category: String;
    constructor Create(Category: string);
  end;

  TMenu<T> = class(TPagingObject<TMenuItem>)
  private
    CurrrentCategory: String;
    procedure FindItemsByCategory;
  protected
    procedure LoadItems; override;
    procedure RefreshUI; override;
  public
    TotalItemList: TList<TMenuItem>;
    procedure ChangeCategory(Category: String);
    Constructor Create(ItemNumberPerPage: integer;
      DisplayAPageProc: IDispAPageItemsProc);
  end;

  TMenuInstructionLink = class
  public
    ParentCode, ChildCode, Description: string;
    Instruction, ChoiceItem, MaximunCharge: boolean;
    SalesPrice, SalesQty: double;
    Constructor Create(q: TClientDataSet);
    // class function GetMenuInstructionLinkList:TList<TMenuInstructionLink>;
  end;

  TInstruction<T> = class(TPagingObject<TMenuInstructionLink>)
  private
    ItemCode: String;
    procedure FindLinksByItemCode;
  protected
    procedure LoadItems; override;
    procedure RefreshUI; override;
  public
    TotalInstructionList: TList<TMenuInstructionLink>;
    procedure ChangeItem(ItemCode: String);
    Constructor Create(ItemNumberPerPage: integer;
      DisplayAPageProc: IDispAPageItemsProc);
  end;

  TCategory<T> = class(TPagingObject<TCategoryItem>)
  protected
    procedure LoadItems; override;
    procedure RefreshUI; override;
  public

  end;

  TOrderItem = class
  private
    // Declare the fields which will be inserted into OrderI Table
    FCondition: integer;
    FPaidQty: double;
    FDescription :string;
    FPriceSelect: double;
    FSeat: integer;
    FOrderNo: String;
    FItemCode: String;
    FQty: double;
    FPrice: double;
    FDiscount: double;
    FTaxRate: double;
    FIDNo: integer;
    FPrintFlag: boolean;
    FSentToKitchen: boolean;
    FVoidReason: String;
    FSpecialOrder: String;
    FCheckListPrinted: boolean;
    FVoidFlag: boolean;
    FOrderOperator: String;
    FOriginalPrice: double;
    FOriginalQty: double;
    FPresetDiscountCode: String;
    // FDescription : String;
    // FItemNumber : String;
  public
    property Condition: integer read FCondition write FCondition;
    property PaidQty: double read FPaidQty write FPaidQty;
    property Description: string read FDescription write FDescription;
    property PriceSelect: double read FPriceSelect write FPriceSelect;
    property Seat: integer read FSeat write FSeat;
    property OrderNo: String read FOrderNo write FOrderNo;
    property ItemCode: String read FItemCode write FItemCode;
    property Qty: double read FQty write FQty;
    property Price: double read FPrice write FPrice;
    property Discount: double read FDiscount write FDiscount;
    property TaxRate: double read FTaxRate write FTaxRate;
    property IDNo: integer read FIDNo write FIDNo;
    property PrintFlag: boolean read FPrintFlag write FPrintFlag;
    property SentToKitchen: boolean read FSentToKitchen write FSentToKitchen;
    property VoidReason: String read FVoidReason write FVoidReason;
    property SpecialOrder: String read FSpecialOrder write FSpecialOrder;
    property CheckListPrinted: boolean read FCheckListPrinted
      write FCheckListPrinted;
    property VoidFlag: boolean read FVoidFlag write FVoidFlag;
    property OrderOperator: String read FOrderOperator write FOrderOperator;
    property OriginalPrice: double read FOriginalPrice write FOriginalPrice;
    property OriginalQty: double read FOriginalQty write FOriginalQty;
    property PresetDiscountCode: String read FPresetDiscountCode
      write FPresetDiscountCode;
    // property Description : String read FDescription write FDescription;
    // property ItemNumber : String read FItemNumber write FItemNumber;
  end;

  TOrder = class
  private
    // Declare the fields which will be inserted into OrderH Table
    FBookingNo: String; // F开始的属性一般作为 private 封装
    FCredit: boolean;
    FOrderPrinted: boolean;
    FTips: double;
    FOrderDate: TDatetime;
    FOrderNo: String;
    FPersons: integer;
    FTableNo: String;
    FServicePerson: String;
    FAmount: double;
    FGST: double;
    FPaidAmount: double;
    FInvoiceNo: String;
    FVipNo: integer;
    FVipName: String;
    FOpName: String;
    FServiceCharge: double;
    FServiceChargeRate: double;
    FSurcharge: double;
    FMachineID: String;
    FBillKind: integer;
    FDollarDiscount: double;
    FDueTime: TDatetime;
    FDiscountKind: integer;
    FDelivery: boolean;
    FOtherCharge: double;
    FOtherChargeRate: double;
    FPriceIncludeGST: boolean;
    FCurrentGSTRate: double;
    FRewardPoints: double;
    FSplitBill: boolean;
    FCustomerName: String;
    FDiscountOperator: String;
    function GetOrderItems: boolean;
    function GetDiscountSumary: double;
  public
    OrderItemList: TList<TOrderItem>;
    property BookingNo: string read FBookingNo write FBookingNo;
    property Credit: boolean read FCredit write FCredit;

    property OrderPrinted: boolean read FOrderPrinted write FOrderPrinted;
    property Tips: double read FTips write FTips;
    property OrderDate: TDatetime read FOrderDate write FOrderDate;
    property OrderNo: string read FOrderNo write FOrderNo;
    property Persons: integer read FPersons write FPersons;
    property TableNo: string read FTableNo write FTableNo;
    property ServicePerson: string read FServicePerson write FServicePerson;
    property Amount: double read FAmount write FAmount;
    property GST: double read FGST write FGST;
    property PaidAmount: double read FPaidAmount write FPaidAmount;
    property InvoiceNo: string read FInvoiceNo write FInvoiceNo;
    property VipNo: integer read FVipNo write FVipNo;
    property VipName: string read FVipName write FVipName;
    property OpName: string read FOpName write FOpName;
    property ServiceCharge: double read FServiceCharge write FServiceCharge;
    property Surcharge: double read FSurcharge write FSurcharge;
    property MachineID: String read FMachineID write FMachineID;
    property BillKind: integer read FBillKind write FBillKind;
    property DollarDiscount: double read FDollarDiscount write FDollarDiscount;
    property DueTime: TDatetime read FDueTime write FDueTime;
    property DiscountKind: integer read FDiscountKind write FDiscountKind;
    property Delivery: boolean read FDelivery write FDelivery;
    property OtherCharge: double read FOtherCharge write FOtherCharge;
    property ServiceChargeRate: double read FServiceChargeRate
      write FServiceChargeRate;

    property OtherChargeRate: double read FOtherChargeRate
      write FOtherChargeRate;
    property PriceIncludeGST: boolean read FPriceIncludeGST
      write FPriceIncludeGST;
    property CurrentGSTRate: double read FCurrentGSTRate write FCurrentGSTRate;
    property RewardPoints: double read FRewardPoints write FRewardPoints;
    property SplitBill: boolean read FSplitBill write FSplitBill;
    property CustomerName: string read FCustomerName write FCustomerName;
    property DiscountOperator: string read FDiscountOperator
      write FDiscountOperator;

    property DiscountAmount: double read GetDiscountSumary;

    // Created: TDateTime;
    procedure InitalData; // 初始化 Order 实例数据，一般和Order界面同时进行
    procedure SaveOrder;
    function SaveIPAdress(TableNo: string): boolean;
    function GetOrderByTabelNo(TableNo: string): boolean;
    constructor Create;
    // procedure AddanOrderList(striGrid: TStringGrid;tableno : String);

  end;

  TTable = class
  private
    FPageNo: integer;
    FX: integer;
    FY: integer;
    FWidth: integer;
    FHeight: integer;
    FTableNo: string;
    FShape: integer;
    FOrderNo: string;
    FDescription: string;
    FIsTable: boolean;
    // FStatus : integer;
    FTableLockerName: string;
    function GetStatus: integer;

  const
    Rate = 1.3;
  public
    property PageNo: integer read FPageNo;
    property X: integer read FX;
    property Y: integer read FY;
    property Width: integer read FWidth;
    property Height: integer read FHeight;
    property TableNo: string read FTableNo;
    property Shape: integer read FShape;
    property OrderNo: string read FOrderNo write FOrderNo;
    property Description: string read FDescription;
    property IsTable: boolean read FIsTable;
    property Status: integer read GetStatus;
    constructor Create(TableNo, OrderNo, Description, TableLockerName: string;
      PageNo, X, Y, Width, Height, Shape, PropertyFlag: integer);
  end;

  TTablePage = class
  private
    FPageNo: integer;
    FDescription: String;
    FTables: TList<TTable>;
  public
    property PageNo: integer read FPageNo;
    property Description: String read FDescription;
    property Tables: TList<TTable> read FTables;
    constructor Create(PageNo: integer; Description: string);
  end;

  TTablePageList = class
  private
    FTablePageList: TList<TTablePage>;
    SelectedPageNo: integer;
    SelectedTableNo: string;
    procedure LoadPages;
    procedure LoadTables;
    function GetTablePageItem(PageNo: integer): TTablePage;
  public
    property TablePageList: TList<TTablePage> read FTablePageList;
    procedure SelectATable(PageNo: integer; TableNo: string);
    procedure RefreshTableStatus;
    constructor Create;
  end;

  TGetOrderbyTable = class
  private
    function GetOrderByTableNo(TableNo: string): TClientDataSet;
  end;

var
  Profile: TProfile;
  ClientDataSet: TClientDataSet;
  proxy: TServerMethods1Client;
  OrderList: TList<TOrder>;

implementation

uses USQLScripts;

{$REGION 'TPagingObject' }

function TPagingObject<T>.SelectAnItem(key: string): T;
begin

end;

procedure TPagingObject<T>.FirstPage;
begin
  CurrentPage := 1;
  RefreshUI;
end;

procedure TPagingObject<T>.PreviousPage;
begin
  if CurrentPage > 1 then
  begin
    CurrentPage := CurrentPage - 1;
    RefreshUI;
  end;
end;

procedure TPagingObject<T>.NextPage;
begin
  if CurrentPage < LastPage then
  begin
    CurrentPage := CurrentPage + 1;
    RefreshUI;
  end;
end;

procedure TPagingObject<T>.CalcLastPage;
begin
  self.LastPage := List.Count div self.ItemNumberPerPage;
  if (List.Count mod self.ItemNumberPerPage) > 0 then
    LastPage := LastPage + 1;
end;

constructor TPagingObject<T>.Create(ItemNumberPerPage: integer;
  DisplayAPageProc: IDispAPageItemsProc);
begin
  inherited Create;
  self.FDisplayAPageProc := DisplayAPageProc;
  self.ItemNumberPerPage := ItemNumberPerPage;
  List := TList<T>.Create;
  LoadItems;
end;

destructor TPagingObject<T>.Destroy;
begin
  List.Free;
  inherited Destroy;
end;

{$ENDREGION}
{$REGION 'TOrder'}
// procedure TOrder.AddanOrderList(striGrid : TStringGrid; tableNo : String);
// var
// OrderItem: TOrderItem;
// Order : TOrder;
// i : integer;
// begin
// //Create Instances
// Order := TOrder.Create;
// OrderItem := TOrderItem.Create;
// OrderItemList := TList<TOrderItem>.Create;
// OrderList :=  TList<TOrder>.Create;
//
//
// //OrderItem part
// for I := 0 to strigrid.RowCount-1 do
// begin
// OrderItem.FItemNumber := striGrid.Cells[0,0];
// OrderItem.FDescription := striGrid.Cells[1,0];
// OrderItem.FCondition := 0;
// OrderItem.FPaidQty := 0;
/// /    OrderItem.FQty := striGrid.Cells[,i];
// OrderItemList.Add(OrderItem);
// end;
//
// //Order part
// Order.FTableNo := tableNo;
// //Add
// OrderList.Add(Order);
// end;

constructor TOrder.Create;
begin
  OrderItemList := TList<TOrderItem>.Create;
end;

function TOrder.GetDiscountSumary: double;
  function CalcItemDiscount(item: TOrderItem): double;
  begin
    case self.DiscountKind of
      1, 2:
        result := item.Price * item.Qty * (1 - item.Discount / 100);
    else
      result := item.Discount;
    end;
  end;

var
  i: integer;
begin
  result := 0;
  for i := 0 to self.OrderItemList.Count - 1 do
  begin
    result := result + CalcItemDiscount(OrderItemList[i]);
  end;
end;

function TOrder.GetOrderByTabelNo(TableNo: string): boolean;
begin
  ClientDataSet.XMLData := proxy.GetOrderByTableNo(TableNo);
  if ClientDataSet.FieldByName('OrderNo').asstring = '' then
    result := false
  else
  begin
    FOrderNo := ClientDataSet.FieldByName('OrderNo').asstring;
    FBookingNo := ClientDataSet.FieldByName('BookingNo').asstring;
    // FCredit := ClientDataSet.FieldByName('Credit').AsBoolean;
    // FOrderPrinted := ClientDataSet.FieldByName('OrderPrinted').AsBoolean;
    // FTips := ClientDataSet.FieldByName('Tips').AsFloat;
    FOrderDate := ClientDataSet.FieldByName('OrderDate').AsDateTime;

    FPersons := ClientDataSet.FieldByName('Persons').AsInteger;
    FTableNo := ClientDataSet.FieldByName('TableNo').asstring;
    // FServicePerson := ClientDataSet.FieldByName('ServicePerson').asstring;
    FAmount := ClientDataSet.FieldByName('Amount').AsFloat;
    FGST := ClientDataSet.FieldByName('GST').AsFloat;
    // FPaidAmount := ClientDataSet.FieldByName('PaidAmount').AsFloat;
    // FInvoiceNo := ClientDataSet.FieldByName('InvoiceNo').asstring;
    // FVipNo := ClientDataSet.FieldByName('VipNo').AsInteger;
    // FVipName := ClientDataSet.FieldByName('VipName').asstring;
    // FOpName := ClientDataSet.FieldByName('OpName').asstring;
    // FServiceCharge := ClientDataSet.FieldByName('ServiceCharge').AsFloat;
    // FServiceChargeRate := ClientDataSet.FieldByName
    // ('ServiceChargeRate').AsFloat;
    // FSurcharge := ClientDataSet.FieldByName('Surcharge').AsFloat;
    // FMachineID := ClientDataSet.FieldByName('MachineID').asstring;
    // FBillKind := ClientDataSet.FieldByName('BillKind').AsInteger;
    FDollarDiscount := ClientDataSet.FieldByName('DollarDiscount').AsFloat;
    // FDueTime := ClientDataSet.FieldByName('DueTime').AsDateTime;
    FDiscountKind := ClientDataSet.FieldByName('DiscountKind').AsInteger;
    // FDelivery := ClientDataSet.FieldByName('Delivery').AsBoolean;
    // FOtherCharge := ClientDataSet.FieldByName('OtherCharge').AsFloat;
    // FOtherChargeRate := ClientDataSet.FieldByName('OtherChargeRate').AsFloat;
    // FPriceIncludeGST := ClientDataSet.FieldByName('PriceIncludeGST').AsBoolean;
    // FCurrentGSTRate := ClientDataSet.FieldByName('CurrentGSTRate').AsFloat;
    // FRewardPoints := ClientDataSet.FieldByName('RewardPoints').AsFloat;
    // FSplitBill := ClientDataSet.FieldByName('SplitBill').AsBoolean;
    // FCustomerName := ClientDataSet.FieldByName('CustomerName').asstring;
    // FDiscountOperator := ClientDataSet.FieldByName('DiscountOperator').asstring;
    result := GetOrderItems;
  end;

end;

function TOrder.GetOrderItems: boolean;
var
  item: TOrderItem;
begin
  self.OrderItemList.Clear;
  try
    ClientDataSet.XMLData := proxy.GetOrderItemByOrderNo(self.OrderNo);
    while not ClientDataSet.eof do
    begin
      item := TOrderItem.Create;
      item.Condition := ClientDataSet.FieldByName('condition').AsInteger;
      item.PaidQty := ClientDataSet.FieldByName('PaidQty').AsFloat;
      item.Description := ClientDataSet.FieldByName('description').asstring;
      item.PriceSelect := ClientDataSet.FieldByName('PriceSelect').AsFloat;
      item.Seat := ClientDataSet.FieldByName('Seat').AsInteger;
      item.OrderNo := ClientDataSet.FieldByName('OrderNo').asstring;
      item.ItemCode := ClientDataSet.FieldByName('itemcode').asstring;
      item.Qty := ClientDataSet.FieldByName('Qty').AsInteger;
      item.Price := ClientDataSet.FieldByName('Price').AsFloat;
      item.Discount := ClientDataSet.FieldByName('Discount').AsFloat;
      item.TaxRate := ClientDataSet.FieldByName('TaxRate').AsFloat;
      item.IDNo := ClientDataSet.FieldByName('IDNo').AsInteger;
      item.PrintFlag := ClientDataSet.FieldByName('PrintFlag').AsBoolean;
      // item.SentToKitchen := ClientDataSet.FieldByName('SendToKitchen').AsBoolean;
      item.VoidReason := ClientDataSet.FieldByName('VoidReason').asstring;
      item.SpecialOrder := ClientDataSet.FieldByName('SpecialOrder').asstring;
      item.CheckListPrinted := ClientDataSet.FieldByName('CheckListPrinted')
        .AsBoolean;
      item.VoidFlag := ClientDataSet.FieldByName('VoidFlag').AsBoolean;
      item.OrderOperator := ClientDataSet.FieldByName('OrderOperator').asstring;
      item.OriginalPrice := ClientDataSet.FieldByName('OriginalPrice').AsFloat;
      item.OriginalQty := ClientDataSet.FieldByName('OriginalQty').AsFloat;
      item.PresetDiscountCode := ClientDataSet.FieldByName
        ('PresetDiscountCode').asstring;

      // FDescription : String;
      // FItemNumber : String;
      OrderItemList.Add(item);
      ClientDataSet.Next;
    end;
    result := true;
  except
    result := false;
  end;
end;

procedure TOrder.InitalData;
begin
  OrderItemList.Clear;
  BookingNo := '';
  Credit := false;
  OrderPrinted := false;
  Tips := 0;
  OrderDate := Now;
  OrderNo := '';
  Persons := 0;
  TableNo := '';
  ServicePerson := '';
  Amount := 0;
  GST := 0;
  PaidAmount := 0;
  InvoiceNo := '';
  VipNo := 0;
  VipName := '';
  OpName := '';
  ServiceCharge := 0;
  Surcharge := 0;
  MachineID := '';
  BillKind := 0;
  DollarDiscount := 0;
  DueTime := 0;
  DiscountKind := 0;
  Delivery := false;
  OtherCharge := 0;
  ServiceChargeRate := 0;
  OtherChargeRate := 0;
  PriceIncludeGST := false;
  CurrentGSTRate := 0;
  RewardPoints := 0;
  SplitBill := false;
  CustomerName := '';
  DiscountOperator := '';
end;

procedure TOrder.SaveOrder;
var
  sql: string;
begin
  sql := TSQLScripts.GetInsertOrderSQL(self);
  proxy.SaveOrder(self.OrderNo, self.TableNo, sql);
end;

function TOrder.SaveIPAdress(TableNo: string): boolean;
begin
  result := proxy.SaveLockName(TableNo);
end;

{$ENDREGION}
{$REGION 'TMenu' }

procedure TMenu<T>.ChangeCategory(Category: String);
begin
  if (Category <> self.CurrrentCategory) then
  begin
    self.CurrrentCategory := Category;
    FindItemsByCategory;
    self.CalcLastPage;
    FirstPage;
  end;
end;

procedure TMenu<T>.FindItemsByCategory;
var
  i: integer;
begin
  self.List.Clear;
  for i := 0 to self.TotalItemList.Count - 1 do
  begin
    if self.CurrrentCategory = TotalItemList.Items[i].Category then
      List.Add(TotalItemList.Items[i])
  end;
end;

// procedure TMenu<T>.LoadItems;
// var
// q: TSQLQuery;
// begin
// q := DataModule1.GetMenuItems;
// while not q.eof do
// begin
// TotalItemList.Add(TMenuItem.Create(q));
// q.Next;
// end;
// q.Close;
// end;

procedure TMenu<T>.LoadItems;
var
  s: string;
begin
  s := ClientModule1.ServerMethods1Client.GetMenuItems;
  // s := proxy.GetMenuItems;
  ClientDataSet.XMLData := s;
  while not ClientDataSet.eof do
  begin
    TotalItemList.Add(TMenuItem.Create(ClientDataSet));
    ClientDataSet.Next;
  end;
end;

procedure TMenu<T>.RefreshUI;
begin
  DisplayAPageMenuProc.DisplayAPageMenuItems(CurrentPage);
end;

constructor TMenu<T>.Create(ItemNumberPerPage: integer;
  DisplayAPageProc: IDispAPageItemsProc);
begin
  self.TotalItemList := TList<TMenuItem>.Create;
  inherited Create(ItemNumberPerPage, DisplayAPageProc);
end;
{$ENDREGION}
{$REGION 'TInstruction' }

procedure TInstruction<T>.ChangeItem(ItemCode: String);
begin
  if (ItemCode <> self.ItemCode) then
  begin
    self.ItemCode := ItemCode;
    FindLinksByItemCode;
    if List.Count > 0 then
    begin
      self.CalcLastPage;
      FirstPage;
    end;
  end;
end;

procedure TInstruction<T>.FindLinksByItemCode;
var
  i: integer;
begin
  self.List.Clear;
  for i := 0 to self.TotalInstructionList.Count - 1 do
  begin
    if self.ItemCode = TotalInstructionList.Items[i].ParentCode then
      List.Add(TotalInstructionList.Items[i])
  end;
end;

procedure TInstruction<T>.LoadItems;
begin
  ClientDataSet.XMLData := proxy.GetMenuInstructionLink;
  while not ClientDataSet.eof do
  begin
    TotalInstructionList.Add(TMenuInstructionLink.Create(ClientDataSet));
    ClientDataSet.Next;
  end;
end;

procedure TInstruction<T>.RefreshUI;
begin
  DisplayAPageMenuProc.DisplayAPageInstructionItems(CurrentPage);
end;

constructor TInstruction<T>.Create(ItemNumberPerPage: integer;
  DisplayAPageProc: IDispAPageItemsProc);
begin
  self.TotalInstructionList := TList<TMenuInstructionLink>.Create;
  inherited Create(ItemNumberPerPage, DisplayAPageProc);
end;
{$ENDREGION}
{$REGION 'TCategory' }

constructor TCategoryItem.Create(Category: string);
begin
  inherited Create;
  self.Category := Category;
end;

procedure TCategory<T>.LoadItems;
begin
  // DataModule := TDataModule1.Create(nil);
  ClientDataSet.XMLData := proxy.GetCategoryItems;

  while not ClientDataSet.eof do
  begin
    List.Add(TCategoryItem.Create(ClientDataSet.FieldByName('category')
      .asstring));
    ClientDataSet.Next;
  end;
  CalcLastPage;
end;

procedure TCategory<T>.RefreshUI;
begin
  DisplayAPageMenuProc.DisplayAPageCategoryItems(CurrentPage);
end;
{$ENDREGION}

/// ////////////////////////////
function TGetOrderbyTable.GetOrderByTableNo(TableNo: string): TClientDataSet;
begin
  ClientDataSet.XMLData := proxy.GetOrderByTableNo(TableNo);
  result := ClientDataSet;
end;
{$REGION 'TMenuItem'}

constructor TMenuItem.Create(q: TClientDataSet);
var
  Multiple: boolean;
begin
  inherited Create;
  self.Code := q.FieldByName('ItemCode').asstring;
  self.Category := q.FieldByName('Category').asstring;
  self.Description := q.FieldByName('Description1').asstring;
  self.TaxRate := q.FieldByName('TaxRate').AsCurrency;
{$IFDEF MSSQL}
  Multiple := q.FieldByName('Multiple').AsBoolean;
{$ELSE}
  Multiple := q.FieldByName('Multiple').AsInteger = 1;
{$ENDIF}
  self.PriceList := TList < TPair < String, Currency >>.Create;
  if Profile.PriceType = TPriceType.Normal then // add normal prices to list
  begin
    if not Multiple then
      PriceList.Add(TPair<String, Currency>.Create(q.FieldByName('Description1')
        .asstring, q.FieldByName('Price').AsCurrency))
    else
    begin
      if q.FieldByName('SubDescription').asstring = '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('Description1').asstring, q.FieldByName('Price')
          .AsCurrency))
      else
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription').asstring, q.FieldByName('Price')
          .AsCurrency));

      if q.FieldByName('SubDescription1').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription1').asstring, q.FieldByName('Price1')
          .AsCurrency));

      if q.FieldByName('SubDescription2').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription2').asstring, q.FieldByName('Price2')
          .AsCurrency));

      if q.FieldByName('SubDescription3').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription3').asstring, q.FieldByName('Price3')
          .AsCurrency));
    end;
  end
  else if Profile.PriceType = TPriceType.Weekend then
  // add weekend prices to list
  begin
    if not Multiple then
      PriceList.Add(TPair<String, Currency>.Create(q.FieldByName('Description1')
        .asstring, q.FieldByName('WeekendPrice').AsCurrency))
    else
    begin
      if q.FieldByName('SubDescription').asstring = '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('Description1').asstring, q.FieldByName('WeekendPrice')
          .AsCurrency))
      else
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription').asstring,
          q.FieldByName('WeekendPrice').AsCurrency));

      if q.FieldByName('SubDescription1').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription1').asstring,
          q.FieldByName('WeekendPrice1').AsCurrency));

      if q.FieldByName('SubDescription2').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription2').asstring,
          q.FieldByName('WeekendPrice2').AsCurrency));

      if q.FieldByName('SubDescription3').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription2').asstring,
          q.FieldByName('WeekendPrice3').AsCurrency));
    end;
  end
  else //
  begin
    if not Multiple then
      PriceList.Add(TPair<String, Currency>.Create(q.FieldByName('Description1')
        .asstring, q.FieldByName('HappyHourPrice1').AsCurrency))
    else
    begin
      if q.FieldByName('SubDescription').asstring = '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('Description1').asstring,
          q.FieldByName('HappyHourPrice1').AsCurrency))
      else
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription').asstring,
          q.FieldByName('HappyHourPrice1').AsCurrency));

      if q.FieldByName('SubDescription1').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription1').asstring,
          q.FieldByName('HappyHourPrice2').AsCurrency));

      if q.FieldByName('SubDescription2').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription2').asstring,
          q.FieldByName('HappyHourPrice3').AsCurrency));

      if q.FieldByName('SubDescription3').asstring <> '' then
        PriceList.Add(TPair<String, Currency>.Create
          (q.FieldByName('SubDescription3').asstring,
          q.FieldByName('HappyHourPrice4').AsCurrency));
    end;
  end;
end;

{$ENDREGION}
{$REGION 'TTablePageList'}

procedure TTablePageList.SelectATable(PageNo: integer; TableNo: string);
begin
  self.SelectedPageNo := PageNo;
  self.SelectedTableNo := TableNo;
end;

procedure TTablePageList.RefreshTableStatus;
var
  i, j: integer;
begin
  for i := 0 to FTablePageList.Count - 1 do
  begin
    for j := FTablePageList.Items[i].Tables.Count - 1 downto 0 do
      FTablePageList.Items[i].Tables[j].Free;
    FTablePageList.Items[i].Tables.Clear;
  end;

  // FTablePageList.Items[i].Tables.Free;
  LoadTables; // 重新从服务器取餐桌信息
end;

constructor TTablePageList.Create;
begin
  self.FTablePageList := TList<TTablePage>.Create;
  LoadPages;
  LoadTables;
end;

procedure TTablePageList.LoadPages;
begin
  ClientDataSet.XMLData := proxy.GetTablePages;
  while not ClientDataSet.eof do
  begin
    FTablePageList.Add(TTablePage.Create(ClientDataSet.FieldByName('PageNo')
      .AsInteger, ClientDataSet.FieldByName('Description').asstring));
    ClientDataSet.Next;
  end;
end;

procedure TTablePageList.LoadTables;
var
  PropertyFlag: integer;
  // PageClientDataSet : TClientDataSet;
begin

  ClientDataSet.XMLData := proxy.GetTableLayout;
  while not ClientDataSet.eof do
  begin
{$IFDEF MSSQL}
    if ClientDataSet.FieldByName('PropertyFlag').AsBoolean then
      PropertyFlag := 1
    else
      PropertyFlag := 0;

    // PageClientDataSet.XMLData := proxy.GetOrderByTableNo(ClientDataSet.FieldByName('TableNo').asstring);

    GetTablePageItem(ClientDataSet.FieldByName('pageflag').AsInteger)
      .Tables.Add(TTable.Create(ClientDataSet.FieldByName('TableNo').asstring,
      ClientDataSet.FieldByName('OrderNo').asstring,
      ClientDataSet.FieldByName('Description').asstring,
      ClientDataSet.FieldByName('TableLockerName').asstring,
      ClientDataSet.FieldByName('pageFlag').AsInteger,
      ClientDataSet.FieldByName('ButtonX').AsInteger,
      ClientDataSet.FieldByName('ButtonY').AsInteger,
      ClientDataSet.FieldByName('ButtonWidth').AsInteger,
      ClientDataSet.FieldByName('ButtonHeight').AsInteger,
      ClientDataSet.FieldByName('ButtonShape').AsInteger, PropertyFlag));
    // add order number to the table

{$ELSE}
    GetTablePageItem(ClientDataSet.FieldByName('pageflag').AsInteger)
      .Tables.Add(TTable.Create(ClientDataSet.FieldByName('TableNo').asstring,
      '', ClientDataSet.FieldByName('Description').asstring,
      ClientDataSet.FieldByName('pageFlag').AsInteger,
      ClientDataSet.FieldByName('ButtonX').AsInteger,
      ClientDataSet.FieldByName('ButtonY').AsInteger,
      ClientDataSet.FieldByName('ButtonWidth').AsInteger,
      ClientDataSet.FieldByName('ButtonHeight').AsInteger,
      ClientDataSet.FieldByName('ButtonShape').AsInteger,
      ClientDataSet.FieldByName('PropertyFlag').AsInteger), PropertyFlag,
      ClientDataSet.FieldByName('Status').AsInteger));
{$ENDIF}
    ClientDataSet.Next; end; end;

    function TTablePageList.GetTablePageItem(PageNo: integer): TTablePage;
    var i: integer; begin result := FTablePageList[0];
    for i := 0 to FTablePageList.Count - 1 do if FTablePageList[i].PageNo = PageNo
    then begin result := FTablePageList[i]; break; end; end;

{$ENDREGION}
{$REGION 'TTable'}
    constructor TTable.Create(TableNo, OrderNo, Description,
      TableLockerName: string;
      PageNo, X, Y, Width, Height, Shape, PropertyFlag: integer);
    begin inherited Create; self.FTableNo := TableNo; self.FOrderNo := OrderNo;
    self.FShape := Shape; self.FX := X; self.FY := Y; self.FWidth := Width;
    self.FHeight := Height; self.FDescription := Description;
    self.FTableLockerName := TableLockerName; FIsTable := (PropertyFlag = 1);

    end;

    function TTable.GetStatus: integer;
    begin if OrderNo = '' then result := 0 else result := 1; end;

{$ENDREGION}
{$REGION 'TTablePage'}
    constructor TTablePage.Create(PageNo: integer;
    Description:
      string); begin inherited Create;

    self.FPageNo := PageNo; self.FDescription := Description;
    self.FTables := TList<TTable>.Create; end;

{$ENDREGION}
{$REGION 'TProfile'}
    constructor TProfile.Create; var weekday: integer;
    settings: TFormatSettings; begin ClientDataSet.XMLData := proxy.GetProfile;
    self.RoundingFlag := ClientDataSet.FieldByName('RoundingFlag').AsInteger;
    self.ServiceChargeRate := ClientDataSet.FieldByName
      ('ServiceChargeRate').AsFloat;

{$IFDEF MSSQL}
    self.ForceVIPDiscount := ClientDataSet.FieldByName('ForceVIPDiscount')
      .AsBoolean; self.NotAllowModify := ClientDataSet.FieldByName
      ('NotAllowModify').AsBoolean;
    self.HappyHour := ClientDataSet.FieldByName('HappyHour').AsBoolean;
    self.EnableWeekendPriceFunction := ClientDataSet.FieldByName
      ('EnableWeekendPriceFunction').AsBoolean;
    self.HappyHourStartTime := ClientDataSet.FieldByName('HappyHourStartTime')
      .AsDateTime; self.HappyHourEndTime := ClientDataSet.FieldByName
      ('HappyHourEndTime').AsDateTime;
{$ELSE}
    settings.DateSeparator := '-'; settings.TimeSeparator := ':';
    settings.ShortDateFormat := 'yyyy-MM-dd';
    settings.ShortTimeFormat := 'hh:mm:ss';

    self.ForceVIPDiscount := (ClientDataSet.FieldByName('ForceVIPDiscount')
      .AsInteger = 1); self.NotAllowModify := (ClientDataSet.FieldByName
      ('NotAllowModify').AsInteger = 1);
    self.HappyHour := (ClientDataSet.FieldByName('HappyHour').AsInteger = 1);
    self.EnableWeekendPriceFunction := (ClientDataSet.FieldByName
      ('EnableWeekendPriceFunction').AsInteger = 1);
    self.HappyHourStartTime := StrToDateTime(ClientDataSet.FieldByName
      ('HappyHourStartTime').asstring, settings);
    self.HappyHourEndTime := StrToDateTime(ClientDataSet.FieldByName
      ('HappyHourEndTime').asstring, settings);
{$ENDIF}
    if HappyHour and (Now >= HappyHourStartTime) and (Now <= HappyHourEndTime)
    then self.FPriceType := TPriceType.HappyHour // happyhour high priority
    else begin weekday := DayOfWeek(Now) - 1;
    if EnableWeekendPriceFunction and (weekday >= ClientDataSet.FieldByName
      ('WeekendPriceStartDay').AsInteger) and (weekday <=
      ClientDataSet.FieldByName('WeekendPriceEndDay').AsInteger)
    then self.FPriceType := TPriceType.Weekend else self.FPriceType :=
      TPriceType.Normal; end; end;
{$ENDREGION}
{$REGION 'TMenuInstructionLink'}
    constructor TMenuInstructionLink.Create(q: TClientDataSet);
    begin self.ParentCode := q.FieldByName('ItemCode').asstring;
    self.ChildCode := q.FieldByName('SubMenuCode').asstring;
    self.SalesPrice := q.FieldByName('SalesPrice').AsFloat;
    self.SalesQty := q.FieldByName('SalesQty').AsFloat;
    self.Description := q.FieldByName('Description1').asstring;
{$IFDEF MSSQL}
    self.Instruction := q.FieldByName('Instruction').AsBoolean;
    self.ChoiceItem := q.FieldByName('ChoiceItem').AsBoolean;
    self.MaximunCharge := q.FieldByName('MaximunCharge').AsBoolean;
{$ELSE}
    self.Instruction := q.FieldByName('Instruction').AsInteger = 1;
    self.ChoiceItem := q.FieldByName('ChoiceItem').AsInteger = 1;
    self.MaximunCharge := q.FieldByName('MaximunCharge').AsInteger = 1;
{$ENDIF}
    end;

    // class function TMenuInstructionLink.GetMenuInstructionLinkList:TList<TMenuInstructionLink>;
    // var
    // list : TList<TMenuInstructionLink>;
    // q : TSQLQuery;
    // begin
    // list := TList<TMenuInstructionLink>.Create;
    // q := DataModule.GetMenuInstructionLink;
    // while not q.eof do
    // begin
    // list.Add(TMenuInstructionLink.Create(q));
    // q.Next;
    // end;
    // q.Close;
    // result := list;
    // end;

{$ENDREGION}
    end.
